#!/usr/bin/env python
# -*- coding: utf-8 -*-
#================================#
# @author: RedSoxFan and NerfSoldier
# @copyright: 2012 RedSoxFan and NerfSoldier
# @license: GNU General Public License v3.0 
#           <http://www.gnu.org/copyleft/gpl.html>
# @summary: Handles Projects
#=======================================#

#=======================================#
# Imports
#=======================================#
# Python
import os
from PyQt4 import QtGui, QtCore
import shutil
# DarkDev
#=======================================#

class Project(QtGui.QTreeWidgetItem):
    def __init__(self,workspace,darkdev,name):
        super(Project,self).__init__(workspace.window.proj)
        self.workspace = workspace
        self.darkdev = darkdev
        self.setText(0,name)
        self.location = os.path.join(workspace.location,name)
        self.setToolTip(0,self.location)
        self.setIcon(0,QtGui.QIcon('resources/project/project.png'))
        self.workspace.window.proj_items[self] = {'r':lambda p,*a,**k:self.proj_menu(p),'proj':lambda:self}
        
    def item_for_path(self,path,start=None):
        '''
        Find QTreeWidgetItem For Path
        '''    
        if not start: start = self
        for child in [start.child(i) for i in range(start.childCount())]:
            if str(child.toolTip(0))==path: return child
            x = self.item_for_path(path, child)
            if x: return x
        return None
    
    @staticmethod
    def create(workspace,darkdev,location=None):
        '''
        Create a project in workspace
        '''
        # Simulate Do-While Loop since python does not support Do-While 
        entry = [location,True]
        while entry[1] and not entry[0]:
            # Prompt User For Project Name
            entry = QtGui.QInputDialog.getText(workspace.window, "Project Name", "What would you like to name the project?")
        # Test To See If User Cancelled
        if not entry[1]: return 
        # Create Project
        p=Project(workspace,darkdev,str(entry[0]))
        # If Folder Does Not Exist Create It And A 'src' subfolder
        if not os.path.exists(os.path.join(workspace.location,str(entry[0]))):
            # Make Project Directory
            os.mkdir(os.path.join(workspace.location,str(entry[0])))
            # Add src Folder
            p.create_folder(p,"src")
        # Sort Project Tree
        p.treeWidget().sortItems(0,QtCore.Qt.AscendingOrder)
        # Refresh Tree
        p.refresh()
    
    def create_file(self,folder,entry=None,template=None):
        '''
        Create File in Folder folder by prompting user for filename and template
        Setting entry keyword will bypass prompting and create file automatically
        Setting template keyword will show template generator for template
        '''
        op = not(bool(entry))
        if not entry:
            # Prompt User For Template
            if template:
                temp = [template,True]
            else:
                temp = QtGui.QInputDialog.getItem(self.workspace.window, "File Type", "What type of file?",sorted(self.darkdev.templates.keys()),current=sorted(self.darkdev.templates.keys()).index('File'),editable=False)
            if temp[1]:
                entry = self.darkdev.templates[str(temp[0])].show(str(folder.toolTip(0)))
                if not entry: return
        # Create QTreeWidgetItem
        fil = QtGui.QTreeWidgetItem(folder)
        fil.setText(0,entry)
        # Calculate Location and Set As Tooltip
        loc = os.path.join(str(folder.toolTip(0)),str(fil.text(0)))
        fil.setToolTip(0,loc)
        if self.darkdev.isimage(loc):
            try:
                if QtGui.QImage().load(loc): fil.setIcon(0,QtGui.QIcon(loc))
                else: fil.setIcon(0,QtGui.QIcon('resources/file/image.png'))
            except:
                fil.setIcon(0,QtGui.QIcon('resources/file/image.png'))
        else:
            fil.setIcon(0,self.darkdev.icon_for(loc))
        # Add Actions To Workspace For File
        self.workspace.window.proj_items[fil] = {'db':lambda p=QtGui.QLabel(): self.workspace.window.open_file(fil,self,parser=str(p.text())),'r':lambda p,*a,**k:self.file_menu(p,fil)}
        # Sort Children
        folder.sortChildren(0,QtCore.Qt.AscendingOrder)
        # Expand Folder Item
        folder.setExpanded(True)
        # Open If Not Auto
        if op and self.darkdev.can_open(loc):
            self.workspace.window.proj_items[fil]['db']()
        return fil
            
    def create_folder(self,folder,name=""):
        '''
        Create Subfolder in Folder folder by prompting user for folder name
        Setting name keyword will bypass prompting and create subfolder automatically
        '''
        # Simulate Do-While Loop since python does not support Do-While 
        entry = [name,True] # Allow Support For Automatic Additions
        while entry[1] and not entry[0]: 
            # Prompt User For Folder Name
            entry = QtGui.QInputDialog.getText(self.workspace.window, "Folder Name", "What would you like to name the folder?")
        # Verify User Did Not Cancel
        if not entry[1]: return
        # Create QTreeWidgetItem
        fol = QtGui.QTreeWidgetItem(folder)
        fol.setText(0,entry[0])
        fol.setIcon(0,QtGui.QIcon('resources/folder/folder.png'))
        # Calculate Absolute File Location and Set As Tooltip
        fol.setToolTip(0,os.path.join(str(folder.toolTip(0)),str(entry[0])))
        # Create Folder
        if not os.path.exists(fol.toolTip(0)): os.mkdir(fol.toolTip(0))
        # Add Actions To Workspace For Folder
        self.workspace.window.proj_items[fol] = {'r':lambda p,*a,**k:self.dir_menu(p,fol)}
        # Sort Children
        folder.sortChildren(0,QtCore.Qt.AscendingOrder)
        # Expand Folder Item
        folder.setExpanded(True)
        return fol
    
    def file_menu(self,point,item):
        '''
        Show Context Menu For QTreeWidgetItem item at QPoint point
        This is for files. For directories use 'dir_menu'
        '''
        menu = QtGui.QMenu(item.text(0))
        # Open Outside
        opno = QtGui.QAction(QtGui.QIcon('resources/file/openoutside.png'),'Open Outside',menu)
        opno.setStatusTip('Open In Default Program')
        opno.triggered.connect(lambda *a,**k:self.workspace.window.open_outside(item))
        menu.addAction(opno)
        # Open
        opn = QtGui.QMenu('Open As')
        opn.setStatusTip('Open')
        opn.triggered.connect(self.workspace.window.proj_items[item]['db'])
        menu.addMenu(opn)
        # Open SubMenus
        for pars in sorted(self.darkdev.parsers.keys()):
            mi = QtGui.QAction(QtGui.QIcon('resources/file/open.png'),"%s"%pars,opn)
            mi.setStatusTip("Open With %s"%pars)
            opn.addAction(mi)
        #
        menu.addSeparator()
        # Delete
        delete = QtGui.QAction(QtGui.QIcon('resources/file/delete.png'),'Delete',menu)
        delete.setStatusTip('Delete')
        delete.triggered.connect(lambda *a,**k:os.remove(str(item.toolTip(0))) or self.clean_folder(item))
        menu.addAction(delete)
        #
        menu.addSeparator()
        # Rename
        rename = QtGui.QAction(QtGui.QIcon('resources/file/rename.png'),'Rename',menu)
        rename.setStatusTip('Rename')
        rename.triggered.connect(lambda *a,**k:self.rename(item))
        menu.addAction(rename)
        # Show
        menu.exec_(self.treeWidget().mapToGlobal(point))
            
    def dir_menu(self,point,item):
        '''
        Show Context Menu For QTreeWidgetItem item at QPoint point
        This is for directories. For files use 'file_menu'
        '''
        menu = QtGui.QMenu(item.text(0))
        # Open Outside
        opno = QtGui.QAction(QtGui.QIcon('resources/file/openoutside.png'),'Open Outside',menu)
        opno.setStatusTip('Open In Default Program')
        opno.triggered.connect(lambda *a,**k:self.workspace.window.open_outside(item))
        menu.addAction(opno)
        #
        menu.addSeparator()
        # New
        new_file = QtGui.QMenu('New')
        new_file.setStatusTip('New')
        new_file.triggered.connect(lambda a:str(a.text())=='Folder' and (self.create_folder(item) or True) or self.create_file(item,template=str(a.text())))
        menu.addMenu(new_file)
        # New Folder
        new_folder = QtGui.QAction(QtGui.QIcon('resources/folder/new.png'),'Folder',new_file)
        new_folder.setStatusTip('New Folder')
        new_file.addAction(new_folder)
        #
        new_file.addSeparator()
        # New SubMenus
        for temp in sorted(self.darkdev.templates.keys()):
            mi = QtGui.QAction(QtGui.QIcon('resources/file/new.png'),"%s"%temp,new_file)
            mi.setStatusTip("New %s"%temp)
            new_file.addAction(mi)
        #
        menu.addSeparator()
        # Delete
        delete = QtGui.QAction(QtGui.QIcon('resources/folder/delete.png'),'Delete',menu)
        delete.setStatusTip('Delete')
        delete.triggered.connect(lambda *a,**k:shutil.rmtree(str(item.toolTip(0))) or self.clean_folder(item))
        menu.addAction(delete)
        #
        menu.addSeparator()
        # Rename
        rename = QtGui.QAction(QtGui.QIcon('resources/folder/rename.png'),'Rename',menu)
        rename.setStatusTip('Rename')
        rename.triggered.connect(lambda *a,**k:self.rename(item))
        menu.addAction(rename)
        # Show
        menu.exec_(self.treeWidget().mapToGlobal(point))
        
    def proj_menu(self,point):
        '''
        Show Context Menu For Project At QPoint point
        '''
        menu = QtGui.QMenu(self.text(0))
        # Open Outside
        opno = QtGui.QAction(QtGui.QIcon('resources/file/openoutside.png'),'Open Outside',menu)
        opno.setStatusTip('Open In Default Program')
        opno.triggered.connect(lambda *a,**k:self.workspace.window.open_outside(self))
        menu.addAction(opno)
        #
        menu.addSeparator()
        # New
        new_file = QtGui.QMenu('New')
        new_file.setStatusTip('New')
        new_file.triggered.connect(lambda a:str(a.text())=='Folder' and (self.create_folder(self) or True) or self.create_file(self,template=str(a.text())))
        menu.addMenu(new_file)
        # New Folder
        new_folder = QtGui.QAction(QtGui.QIcon('resources/folder/new.png'),'Folder',new_file)
        new_folder.setStatusTip('New Folder')
        new_file.addAction(new_folder)
        #
        new_file.addSeparator()
        # New SubMenus
        for temp in sorted(self.darkdev.templates.keys()):
            mi = QtGui.QAction(QtGui.QIcon('resources/file/new.png'),"%s"%temp,new_file)
            mi.setStatusTip("New %s"%temp)
            new_file.addAction(mi)
        #
        menu.addSeparator()
        # Refresh
        refresh = QtGui.QAction('Refresh',menu)
        refresh.setStatusTip('Refresh')
        refresh.triggered.connect(self.refresh)
        menu.addAction(refresh)
        #
        menu.addSeparator()
        # Delete
        delete = QtGui.QAction(QtGui.QIcon('resources/project/delete.png'),'Delete',menu)
        delete.setStatusTip('Delete')
        delete.triggered.connect(lambda *a,**k:shutil.rmtree(str(self.toolTip(0))) or self.clean())
        menu.addAction(delete)
        #
        menu.addSeparator()
        # Rename
        rename = QtGui.QAction(QtGui.QIcon('resources/project/rename.png'),'Rename',menu)
        rename.setStatusTip('Rename')
        rename.triggered.connect(lambda *a,**k:self.rename(self))
        menu.addAction(rename)
        #
        menu.addSeparator()
        # Close Project
        close = QtGui.QAction(QtGui.QIcon('resources/project/close.png'),'Close Project',menu)
        close.setStatusTip('Close Project')
        close.triggered.connect(lambda *a,**k:self.clean())
        menu.addAction(close)
        # Show
        menu.exec_(self.treeWidget().mapToGlobal(point))
        
    def rename(self,item):
        '''
        Rename Item
        '''
        path = str(item.toolTip(0))
        old, dr = os.path.basename(path), os.path.dirname(path)
        # Prompt User For Project Name
        entry = QtGui.QInputDialog.getText(self.workspace.window, "Rename", "What would you like to rename %s to?"%old,text=old)
        # Verify User Did Not Cancel and Filename is valid
        if not(entry[0] and entry[1]): return
        # Rename
        os.rename(path,os.path.join(dr,str(entry[0])))
        item.setText(0,entry[0])
        item.setToolTip(0,os.path.join(dr,str(entry[0])))
        if item==self: self.location=str(item.toolTip(0))
        # Check If File or Folder/Project
        if os.path.isfile(str(item.toolTip(0))):
            # Fix Double Click Event
            loc = str(item.toolTip(0))
            if self.darkdev.isimage(loc):
                try:
                    if QtGui.QImage().load(loc): item.setIcon(0,QtGui.QIcon(loc))
                    else: item.setIcon(0,QtGui.QIcon('resources/file/image.png'))
                except:
                    item.setIcon(0,QtGui.QIcon('resources/file/image.png'))
            else:
                item.setIcon(0,self.darkdev.icon_for(loc))
            self.workspace.window.proj_items[item]['db'] = lambda p=QtGui.QLabel(): self.workspace.window.open_file(item,self,parser=str(p.text()))
            # Check For Open Tabs
            for i in range(self.workspace.window.tabs.count()):
                if str(self.workspace.window.tabs.tabToolTip(i))==str(path):
                    self.workspace.window.tabs.removeTab(i)
                    self.workspace.window.open_file(item,self,i)
                    break
        else:
            # Rename Children
            self.__fix_filepaths(item, path)
    
    def __fix_filepaths(self,item,path):
        for child in [item.child(i) for i in range(item.childCount())]:
            oloc = str(child.toolTip(0))
            loc = oloc.replace(path,str(item.toolTip(0)))
            child.setToolTip(0,loc)
            for i in range(self.workspace.window.tabs.count()):
                if str(self.workspace.window.tabs.tabToolTip(i))==oloc: 
                    self.workspace.window.tabs.removeTab(i)
                    self.workspace.window.open_file(child,self,i)
                    break
            if os.path.isfile(loc):
                self.workspace.window.proj_items[child]['db'] = lambda *a,**k: self.workspace.window.open_file(child,self)
            self.__fix_filepaths(child, os.path.join(str(path),str(child.text(0))))
            
    @staticmethod
    def load(workspace,darkdev,location):
        '''
        Load project for location
        '''
        return Project.create(workspace, darkdev, location)
    
    def clean(self,rmself=True):
        '''
        Clean Up Project
        '''
        return self.clean_folder(self,rmself=rmself)
    
    def clean_folder(self,item,rmself=True):
        '''
        Clean Up Project
        '''
        # Remove Project Tree
        loc = str(item.toolTip(0))
        if rmself:
            parent = item.parent()
            if not parent: parent = self.treeWidget().invisibleRootItem()
            index = parent.indexOfChild(item)
            parent.takeChild(index)
        else: 
            self.takeChildren() 
        # Close All Open Tabs From This Folder
        i=0
        open_tabs = []
        while i < self.workspace.window.tabs.count():
            if str(self.workspace.window.tabs.tabToolTip(i)).startswith(loc):
                open_tabs += [(i,str(self.workspace.window.tabs.tabToolTip(i)),str(self.workspace.window.tabs.tabWhatsThis(i)))]
                self.workspace.window.tabs.removeTab(i)
            else: i+=1
            
        return open_tabs
            
    def refresh(self,*a,**k):
        open_tabs = self.clean(rmself=False) # Clean But Do Not Remove Self
        self.workspace.window.proj.add_dir(self.location,self,self)
        m = 0
        for i,t,p in open_tabs:
            if os.path.exists(t): self.workspace.window.open_file(self.item_for_path(t),self,i-m,p)
            else: m+=1